//
//  MTProviderType.swift
//  MT
//
//  Created by zhang qiang on 2019/4/28.
//  Copyright © 2019 DDC. All rights reserved.
//

import Foundation
import Moya
import ObjectMapper
import Result

public protocol MTProviderType: AnyObject {
    associatedtype Target: MTTargetType
    
    /// 模型化数据请求
    func request<ModelType: Mappable>(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping (_ result: Result<ModelType, AnyError>, _ isCache: Bool) -> Void ) -> Cancellable
    
    /// 返回最原始的请求数据
    func originRequest(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping (_ result: Result<Response, AnyError>, _ isCache: Bool) -> Void ) -> Cancellable
}

public class MTProvider<Target: MTTargetType>: MTProviderType {
    
    #if DEBUG
    /// 修改这个变量添加默认plugin
    private var defaultPlugins: [PluginType] = [
        MTNetworkLogPlugin(verbose: true),
        MTTokenInvPlugin()
    ]
    #else
    /// 修改这个变量添加默认plugin
    private var defaultPlugins: [PluginType] = [
        MTNetworkLogPlugin(verbose: true),
        MTTokenInvPlugin()
    ]
    #endif
    
    private let moyaProvider: MoyaProvider<Target>
    
    // 这里最好替换成YYCache, 并做成单例的cache，这里只是做个样子
    private let cache = NSCache<NSString, NSData>()
    
    public init(manager: Manager? = Manager.default, timeoutIntervalForRequest: TimeInterval? = 15, plugins: [PluginType]? = []) {
        
//        manager.session.configuration.timeoutIntervalForRequest = timeoutIntervalForRequest!
//
//        //moyaProvider = MoyaProvider<Target>.init(manager: manager, plugins: defaultPlugins + plugins!)
//        moyaProvider = MoyaProvider<Target>(session: manager, plugins: defaultPlugins + plugins!)
        manager!.session.configuration.timeoutIntervalForRequest = timeoutIntervalForRequest!
        
        moyaProvider = MoyaProvider<Target>.init(manager: manager!, plugins: defaultPlugins + plugins!)
        
    }
    
    
    
    
    @discardableResult
    public func request<ModelType>(_ target: Target,
                                   callbackQueue: DispatchQueue?,
                                   progress: ProgressBlock?,
                                   completion: @escaping (Result<ModelType, AnyError>, Bool) -> Void) -> Cancellable where ModelType : Mappable {
        
        typealias RequestResult = Result<ModelType, AnyError>
        
        if target.needCache {
            
            if target.cachePolicy != .reloadIgnoringLocalCacheData{
                if let cacheData = self.cache.object(forKey: NSString(string: target.cacheId())){
                    let fakeResponse = Response(statusCode: 0, data: cacheData as Data)
                    
                    self.handle(response: fakeResponse, isCache: true, completion: completion)
                    
                }else{
                    completion(RequestResult.init(error: AnyError(MTError.notFoundCacheError)), true)
                }
            }
            
            if target.cachePolicy == .retutnCacheDataDontLoad{
                return NoCancellable()
            }
            
        }
        
        return moyaProvider.request(target, callbackQueue: callbackQueue, progress: progress) { [weak self] (result) in
            
            guard let `self` = self else { return }
            
            switch result {
            case .success(let response):
                
                self.handle(response: response, isCache: false, completion: { (result: Result<ModelType, AnyError>, iscache: Bool) in
                    
                    completion(result, iscache)
                    
                    switch result{
                    case .success(_):
                        //  只有成功的情况下 保存缓存数据
                        if target.needCache {
                            self.cache.setObject(NSData(data: response.data) , forKey: NSString(string: target.cacheId()))
                        }
                        
                    default:
                        break
                    }
                    
                })
                
            case .failure(let error):
                completion( RequestResult.init(error: AnyError(error)), false)
            }
            
        }
        
    }
    
    @discardableResult
    public func originRequest(_ target: Target, callbackQueue: DispatchQueue?, progress: ProgressBlock?, completion: @escaping (Result<Response, AnyError>, Bool) -> Void) -> Cancellable {
        
        typealias RequestResult = Result<Response, AnyError>
        
        if target.needCache {
            
            if target.cachePolicy != .reloadIgnoringLocalCacheData{
                if let cacheData = self.cache.object(forKey: NSString(string: target.cacheId())){
                    let fakeResponse = Response(statusCode: 0, data: cacheData as Data)
                    
                    self.originHandle(response: fakeResponse, isCache: true, completion: completion)
                    
                }else{
                    completion(RequestResult.init(error: AnyError(MTError.notFoundCacheError)), true)
                }
            }
            
            if target.cachePolicy == .retutnCacheDataDontLoad{
                return NoCancellable()
            }
            
        }
        
        return moyaProvider.request(target, callbackQueue: callbackQueue, progress: progress) { [weak self] (result) in
            
            guard let `self` = self else { return }
            
            switch result {
            case .success(let response):
                
                self.originHandle(response: response, isCache: false, completion: { (result: Result<Response, AnyError>, iscache: Bool) in
                    
                    completion(result, iscache)
                    
                    switch result{
                    case .success(_):
                        //  只有成功的情况下 保存缓存数据
                        if target.needCache{
                            self.cache.setObject(NSData(data: response.data) , forKey: NSString(string: target.cacheId()))
                        }
                        
                    default:
                        break
                    }
                    
                })
                
            case .failure(let error):
                completion( RequestResult.init(error: AnyError(error)), false)
            }
            
        }
        
    }
    
    // 处理reponse
    private func handle<ModelType: Mappable>(response: Response, isCache: Bool, completion: @escaping (_ result: Result<ModelType, AnyError>, _ isCache: Bool) -> Void) {
        
        do {
            
            let model = try response.mapCommonModel() as ModelType
            
            completion( Result<ModelType, AnyError>.init(value: model), isCache)
            
        }catch{
            completion( Result<ModelType, AnyError>.init(error: AnyError(error) ), isCache)
        }
    }
    
    
    // 处理reponse
    private func originHandle(response: Response, isCache: Bool, completion: @escaping (_ result: Result<Response, AnyError>, _ isCache: Bool) -> Void) {
        completion( Result<Response, AnyError>.init(value: response), isCache)
    }
}
